# -*- coding: utf-8 -*-
from __future__ import absolute_import

import requests

try:
    from urllib.parse import urlencode
except ImportError:
    from urllib import urlencode
import json
import time
from src.models.auth import AuthInstance, SocialAccount
from src.utils.models import db


class BaseClient(object):
    def __init__(self, client_id, client_secret, token=None):
        # const define
        self.windowName = ''
        self.provider = ''
        self.redirect_uri = ''
        # client configure
        self.authorization_url = ''
        self.grant_url = ''
        self.api_url = ''
        self.response_type = ''
        self.grant_type = ''

        # init basic info
        self.client_id = client_id
        self.client_secret = client_secret

        # client scope
        self.scope = {'base': ''}

        # activate client directly if given token
        if token:
            self.set_token(token)

    def setWindowName(self, windowName):
        self.windowName = windowName

    def setRedirectUri(self, redirect_uri):
        self.redirect_uri = redirect_uri

    @property
    def authorize_url(self):
        params = {
            'client_id': self.client_id,
            'response_type': self.response_type,
            'scope': self.scope['email'],
            'redirect_uri': self.redirect_uri
        }
        return "{0}?{1}".format(self.authorization_url, urlencode(params))

    @property
    def alive(self):
        if self.expires_at:
            return self.expires_at > time.time()
        else:
            return False

    def set_code(self, windowName):
        """Activate client by authorization_code.
        """
        self.windowName = windowName
        ai = db.session.query(AuthInstance) \
            .filter(AuthInstance.window_name == self.windowName) \
            .filter(AuthInstance.provider == self.provider) \
            .filter(AuthInstance.is_logout == 1) \
            .first()
        if ai is None:
            raise RuntimeError('this database instance don''t have this windowname,do not set code')
        params = {
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'grant_type': 'authorization_code',
            'code': ai.code,
            'redirect_uri': self.redirect_uri
        }
        res = requests.post(self.grant_url, data=params)
        token = json.loads(res.text)
        self._assert_error(token)

        token[u'expires_at'] = int(time.time()) + int(token.pop(u'expires_in'))
        self.set_token(token)

    def set_token(self, token):
        """Directly activate client by access_token.
        """
        self.token = token
        ai = db.session.query(AuthInstance) \
            .filter(AuthInstance.window_name == self.windowName) \
            .filter(AuthInstance.provider == self.provider) \
            .filter(AuthInstance.is_logout == 1) \
            .first()
        if ai is None:
            raise RuntimeError('this database instance don''t have this windowname')
        ai.provider = self.provider
        ai.provider_id = token['uid']
        self.provider_id = token['uid']
        ai.access_token = token['access_token']
        ai.expires_at = token['expires_at']

        self.session.params = {'access_token': self.access_token}

    def check_user(self):
        acc = db.session.query(SocialAccount) \
            .filter(SocialAccount.provider_id == self.provider_id) \
            .filter(AuthInstance.provider == self.provider) \
            .filter(AuthInstance.is_logout == 1) \
            .first()
        if acc is None:
            acN = SocialAccount(provider_id=self.provider_id, provider=self.provider)
            db.session.add(acN)
            db.session.commit()

    def _assert_error(self, d):
        """Assert if json response is error.
        """
        if 'error_code' in d and 'error' in d:
            raise RuntimeError("{0} {1}".format(
                d.get("error_code", ""), d.get("error", "")))

    def get(self, uri, **kwargs):
        """Request resource by get method.
        """
        url = "{0}{1}.json".format(self.api_url, uri)

        # for username/password client auth
        if self.session.auth:
            kwargs['source'] = self.client_id

        res = json.loads(self.session.get(url, params=kwargs).text)
        self._assert_error(res)
        return res

    def post(self, uri, **kwargs):
        """Request resource by post method.
        """
        url = "{0}{1}.json".format(self.api_url, uri)

        # for username/password client auth
        if self.session.auth:
            kwargs['source'] = self.client_id

        if "pic" not in kwargs:
            res = json.loads(self.session.post(url, data=kwargs).text)
        else:
            files = {"pic": kwargs.pop("pic")}
            res = json.loads(self.session.post(url,
                                               data=kwargs,
                                               files=files).text)
        self._assert_error(res)
        return res
